package fingerprint;
/**
 * Created by guolanqing on 16/11/3.
 */

import java.util.ArrayList;

import static fingerprint.FFT.WINDOW_SIZE;

public class FingerPrint {

    // DO NOT write FFT.WINDOW_SIZE / 44100, it equals to 0 forever!!
    public static final double scaling = WINDOW_SIZE / 44100.0;

    public static final int N = 3;

    public ArrayList<int[]> constel_data = new ArrayList<>();
    private int id;

    /**
     * For songs about to add into DB
     *
     * @param id
     */
    public FingerPrint(int id) {
        this.id = id;
    }

    /**
     * For songs about to be searched
     */
    public FingerPrint() {
        this.id = -1;
    }

    /**
     * Append a column of frequency peaks to the constellation map.
     * A frequency peak is a frequency value whose amplitude is the highest among
     * all frequencies in a frequency interval.
     *
     * @param freqDomain The frequency domain generated by FFT.
     */

    public void append(double[] freqDomain) {
        int[] freqPeaks = new int[N];

        ArrayList<Integer> topN = new ArrayList<Integer>();
        topN = SelectSort(freqDomain, freqDomain.length );
        for (int i = 0; i < N; i++) {
            freqPeaks[i] = topN.get(i);
        }
        constel_data.add(freqPeaks);
    }

    /**
     * Generate fingerprints using Combinational Hash.
     * For each frequency peak, generate 6 fingerprints with its 6 successors.
     *
     * @return
     */
    public ArrayList<ShazamHash> combineHash() {
        if (constel_data.size() < 3)
            throw new RuntimeException("Too few frequency peaks");
        ArrayList<ShazamHash> hashes = new ArrayList<>();
        for (int i = 0; i < constel_data.size() - 2; ++i) {
            for (int k = 0; k < N; ++k) {

                // "Combine" with all peak frequencies inside its next two frames.
                for (int j = 1; j <= 2; ++j) {
                    for (int kk = 1; kk < N; ++kk) {
                        ShazamHash hash = new ShazamHash();
                        // hash.f1 = (short) constel_data.get(i)[k];
                        // hash.f2 = (short) constel_data.get(i + j)[kk];
                        // hash.dt = (short) j;
                        int f1 = (short) constel_data.get(i)[k];
                        int f2 = (short) constel_data.get(i + j)[kk];
                        int dt = (short) j;
                        hash.offset = i+1;
                       // hash.id = id;
                        // finger_id is a new hash value
                        hash.finger_id = (dt << 18) | (f1 << 9) | f2;
                        hashes.add(hash);
                    }
                }
            }
        }
        return hashes;
    }


    //select N max numbers from a double array
    static ArrayList<Integer> SelectSort(double a[], int len) {
        ArrayList<Integer> num = new ArrayList<Integer>();
        double temp;
        int nIndex = 0;
        for (int i = 0; i < len - 1; i++) {
            nIndex = i;
            for (int j = i + 1; j < len; j++) {
                if (a[j] > a[nIndex]) {
                    nIndex = j;
                }
            }
            num.add(nIndex * ((int) 44099.0 / 4095) + 1);
            if (nIndex != i) {
                temp = a[i];
                a[i] = a[nIndex];
                a[nIndex] = temp;
            }
        }
        return num;
    }
}